#coding:utf-8

import Config;
import wx;
import os;
import wx.lib.rcsizer as rcs;
import wx.lib.intctrl;
import Global;
import Loading;
import string;
import ELog;
import traceback;
import array;
import time;
import numpy;  
import matplotlib;
import matplotlib.figure;
import matplotlib.backends.backend_wxagg;
import matplotlib.backends.backend_wx;

try:
    from agw import fourwaysplitter as FWS
except ImportError: # if it's not there locally, try the wxPython lib.
    try:
        import wx.lib.agw.fourwaysplitter as FWS
    except ImportError:
        exit()

#菜单ID
from AppIds import *

import SerialUtil
import wx.lib.newevent

#Frame start
SERIAL_GENIUS_FS_CHAR=0x0F;
#Slash
SERIAL_GENIUS_SL_CHAR=0x1F;



#超级终端
class Terminal(wx.Panel):
    def __init__(self, parent):
        wx.Panel.__init__(self, parent, style=wx.BORDER_DOUBLE);

        ############# 状态信息 ############
        #该模块是否启用
        self.isUsing=False;

        ############# 配置信息 ############
        self.sendLineBreak=False;
        self.ShowCharset=0;
        self.UnshowChars = [];
        self.UnsentBuffer = [];
        self.AllowChars = [];
        self.AllowChars.extend([10,13]);        # \r \n
        self.AllowChars.extend([8]);            # backspace
        self.AllowChars.extend([4]);            # Ctrl-D
        self.AllowChars.extend(range(32,127));
        
        ############# 开始布局 ###########
        gridSizer = rcs.RowColSizer();
        
        rowTotal=3;
        colTotal=4;

        #标题
        title=wx.StaticText(self, -1, u"超级终端");
        title.SetFont(wx.Font(8, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD ));
        gridSizer.Add(title, pos=(0,0), size=(1, colTotal), flag=wx.EXPAND|wx.ALIGN_CENTER_VERTICAL);
        
        #变量显示区域
        self.dataTerminal = wx.TextCtrl(self, style=wx.TE_MULTILINE);
        #caret = wx.Caret(self,1,100);
        #self.dataTerminal.SetCaret(caret);
        self.dataTerminal.SetFont(wx.Font(10, wx.FONTFAMILY_TELETYPE, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL ));
        self.dataTerminal.Bind(wx.EVT_MOUSE_EVENTS, self.OnMouseEvents)
        self.dataTerminal.Bind(wx.EVT_CHAR, self.OnChar);
        gridSizer.Add(self.dataTerminal, pos=(1,0), size=(1, colTotal), flag=wx.EXPAND|wx.ALIGN_CENTER_VERTICAL);
        
        
        #清空
        btn = wx.Button(self, -1, u"清空")
        self.Bind(wx.EVT_BUTTON, self.OnClearData, btn);
        gridSizer.Add(btn, pos=(2, 0), size=(1,1), flag=wx.ALIGN_CENTER_VERTICAL);

        
        #保存
        btn = wx.Button(self, -1, u"保存数据")
        self.Bind(wx.EVT_BUTTON, self.OnSaveData, btn);
        gridSizer.Add(btn, pos=(2, 1), size=(1,1), flag=wx.ALIGN_CENTER_VERTICAL);
        
        #显示模式
        ck=wx.CheckBox(self, -1, u"同时发送换行符");
        ck.Bind(wx.EVT_CHECKBOX, self.OnSendLineBreak);
        gridSizer.Add(ck, pos=(2, 2), size=(1,1), flag=wx.EXPAND|wx.ALIGN_CENTER_VERTICAL);
        if Config.APP_CONFIG['module_terminal']['sendNewLine']=='1':
            ck.SetValue(True);
            self.sendLineBreak=True;

        #字符编码
        charsets=[u'ASCII编码',u'UTF8编码',u'GBK编码'];
        comboBox = wx.ComboBox(self, -1, choices=charsets, style=wx.CB_READONLY|wx.CB_DROPDOWN)
        gridSizer.Add(comboBox, pos=(2, 3), size=(1,1), flag=wx.EXPAND|wx.ALIGN_CENTER_VERTICAL);
        comboBox.Bind(wx.EVT_COMBOBOX, self.OnCharsetChange)
        comboBox.SetStringSelection(charsets[0]);
        if Config.APP_CONFIG['module_terminal']['charset']=='0':
            comboBox.SetSelection(0);
            self.ShowCharset=0;
        elif Config.APP_CONFIG['module_terminal']['charset']=='1':
            comboBox.SetSelection(1);
            self.ShowCharset=1;
        elif Config.APP_CONFIG['module_terminal']['charset']=='2':
            comboBox.SetSelection(2);
            self.ShowCharset=2;
        
    
        #设置变宽信息
        gridSizer.AddGrowableCol(2);
        gridSizer.AddGrowableRow(1);


        ############# 结束布局 ###########
        self.SetSizer(gridSizer)
        self.SetAutoLayout(True)
        
        #更新GUI数据的线程
        (self.UpdateDataEvent, self.UPDATA_DATA_EVENT) = wx.lib.newevent.NewEvent();
        self.Bind(self.UPDATA_DATA_EVENT, self.SaveUpdateWindow)

    def OnChar(self, event):
        if event.m_keyCode not in self.AllowChars:
            #print(event.m_keyCode);
            return;
        
        if event.m_keyCode not in [13,8]:
            self.UnsentBuffer.append(event.m_keyCode);
            
        if event.m_keyCode == 4:
            self.dataTerminal.write('^');
        elif event.m_keyCode == 8:
            self.UnsentBuffer.pop();
        
        if Global.SerialGeniusPort and Global.SerialGeniusPort.running:
            event.Skip();
            if event.m_keyCode==13:
                Global.SerialGeniusPort.SendAppend(self.UnsentBuffer);
                self.UnsentBuffer = [];
                if self.sendLineBreak:
                    Global.SerialGeniusPort.SendAppend([0x0D]);
                    Global.SerialGeniusPort.SendAppend([0x0A]);

        else:
            wx.MessageBox(u'请先打开串口',u'提示');

    # prevent mouse events
    def OnMouseEvents(self, event):
        self.dataTerminal.SetFocus();
        return;
        if event.m_rightDown:
            event.Skip();


    #保存数据
    def OnSaveData(self, event):
        self.dataTerminal.SaveFile(time.strftime('Terminal_%Y%m%d_%H_%M_%S.txt',time.localtime(time.time())));
        wx.MessageBox(u'保存成功');
    
    #字符集更改
    def OnCharsetChange(self, event):
        self.ShowCharset=event.GetSelection();
        Config.APP_CONFIG['module_terminal']['charset']=str(self.ShowCharset);
    
    #新增串口数据，被串口接收所调用，这里的长度必须指定，否则字符串到\0将会终止
    def RecvData(self, data):
        self.DataWindowAppend(data);
        
    
    #新增加数据到显示窗口，这里的长度必须指定，否则字符串到\0将会终止
    def DataWindowAppend(self, data):
        dataStr=[];
        length=len(data);
        if self.ShowCharset==0:
            #ASCII编码
            for i in range(0, length):
                d=data[i];
                if d==0x0A or 32<=d<127:
                    dataStr.append(chr(d));
                    
        elif self.ShowCharset==1:
            #UTF8编码
            #由于UTF8编码是变长编码，所以将数据先读入self.UnshowChars然后识别编码
            self.UnshowChars.extend(data);
            #1字节 0xxxxxxx   
            #2字节 110xxxxx 10xxxxxx   
            #3字节 1110xxxx 10xxxxxx 10xxxxxx   
            #4字节 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx   
            #5字节 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx   
            #6字节 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
            oneChar=[];
            while len(self.UnshowChars)>0:
                
                firstChar=self.UnshowChars[0];
                
                del oneChar[:];
                
                if (0xFE & firstChar)==0xFC:
                    if len(self.UnshowChars)>=6:
                        for i in range(0,6):
                            oneChar.append(chr(self.UnshowChars.pop(0)));
                    else:
                        break;
                
                elif (0xFC & firstChar)==0xF8:
                    if len(self.UnshowChars)>=5:
                        for i in range(0,5):
                            oneChar.append(chr(self.UnshowChars.pop(0)));
                    else:
                        break;
                
                elif (0xF8 & firstChar)==0xF0:
                    if len(self.UnshowChars)>=4:
                        for i in range(0,4):
                            oneChar.append(chr(self.UnshowChars.pop(0)));
                    else:
                        break;
                
                elif (0xF0 & firstChar)==0xE0:
                    if len(self.UnshowChars)>=3:
                        for i in range(0,3):
                            oneChar.append(chr(self.UnshowChars.pop(0)));
                    else:
                        break;
                
                elif (0xE0 & firstChar)==0xC0:
                    if len(self.UnshowChars)>=2:
                        for i in range(0,2):
                            oneChar.append(chr(self.UnshowChars.pop(0)));
                    else:
                        break;
                
                elif (0x80 & firstChar)==0x00:
                    if len(self.UnshowChars)>=1:
                        for i in range(0,1):
                            char=self.UnshowChars.pop(0);
                            if char==0x0A or 32<=char<127:
                                oneChar.append(chr(char));
                    else:
                        break;

                else:
                    self.UnshowChars.pop(0);
                    

                if len(oneChar)>0:
                    try:
                        dataStr.append(unicode(''.join(oneChar), 'utf-8'));
                    except:
                        pass;
                else:
                    pass;
                
        elif self.ShowCharset==2:
            #GBK编码

            #先存起来
            self.UnshowChars.extend(data);
            
            while len(self.UnshowChars)>0:
                oneChar=[];
                char = self.UnshowChars.pop(0);
                if char > 0x80:
                    if len(self.UnshowChars)<2:
                        break
                    oneChar.append(chr(char));
                    char = self.UnshowChars[0];
                    oneChar.append(chr(char));
                    try:
                        wchar=(''.join(oneChar)).decode('GB2312','ignore');
                        dataStr.append(wchar);
                        self.UnshowChars.pop(0);
                    except:
                        dataStr.append('?');
                    
                elif char==0x0A or 32<=char<127:
                    dataStr.append(chr(char)); 
                
        if len(dataStr)>0:   
            wx.PostEvent(self,self.UpdateDataEvent(data=''.join(dataStr)));
        
    #新增数据    
    def SaveUpdateWindow(self, evt):
        self.dataTerminal.SetInsertionPointEnd();
        self.dataTerminal.WriteText(evt.data);

    #清空数据
    def OnClearData(self, event):
        self.dataTerminal.SetValue('');

    #同时发送换行符
    def OnSendLineBreak(self, event):
        self.sendLineBreak=event.GetEventObject().GetValue();
        if self.sendLineBreak:
            Config.APP_CONFIG['module_terminal']['sendNewLine']='1';
        else:
            Config.APP_CONFIG['module_terminal']['sendNewLine']='0';
        
    #保存设置
    def SaveUpdateWindow(self, evt):
        self.dataTerminal.SetInsertionPointEnd();
        self.dataTerminal.WriteText(evt.data);
    
            
        